package com.dream.auth.security.converter;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomAccessTokenRequestConverter implements AuthenticationConverter {

	private static final String ACCESS_TOKEN_REQUEST_ERROR_URI =
			"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

	@Override
	public Authentication convert(HttpServletRequest request) {
		// 授权类型是必需的
		String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
		if (!StringUtils.hasText(grantType)) {
			throw new OAuth2AuthenticationException(new OAuth2Error("invalid_request",
					"OAuth 2.0 Parameter: " + OAuth2ParameterNames.GRANT_TYPE, ACCESS_TOKEN_REQUEST_ERROR_URI));
		}

		// 根据授权类型创建相应的认证token
		if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(grantType)) {
			return createAuthorizationCodeAuthentication(request);
		} else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) {
			return createRefreshTokenAuthentication(request);
		} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(grantType)) {
			return createClientCredentialsAuthentication(request);
		}

		log.warn("Unsupported grant_type: {}", grantType);
		throw new OAuth2AuthenticationException(new OAuth2Error("unsupported_grant_type",
				"Unsupported grant_type: " + grantType, ACCESS_TOKEN_REQUEST_ERROR_URI));
	}

	private Authentication createAuthorizationCodeAuthentication(HttpServletRequest request) {
		// code 和 redirect_uri 是必需的
		String code = request.getParameter(OAuth2ParameterNames.CODE);
		String redirectUri = request.getParameter(OAuth2ParameterNames.REDIRECT_URI);
		if (!StringUtils.hasText(code) || !StringUtils.hasText(redirectUri)) {
			throw new OAuth2AuthenticationException(new OAuth2Error("invalid_request", "OAuth 2.0 Parameter(s) missing",
					ACCESS_TOKEN_REQUEST_ERROR_URI));
		}

		Map<String, Object> additionalParameters = new HashMap<>();
		// 添加 PKCE 参数
		String codeVerifier = request.getParameter(PkceParameterNames.CODE_VERIFIER);
		if (StringUtils.hasText(codeVerifier)) {
			additionalParameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);
		}

		return new OAuth2AuthorizationCodeAuthenticationToken(code, null, redirectUri, additionalParameters);
	}

	private Authentication createRefreshTokenAuthentication(HttpServletRequest request) {
		// refresh_token 是必需的
		String refreshToken = request.getParameter(OAuth2ParameterNames.REFRESH_TOKEN);
		if (!StringUtils.hasText(refreshToken)) {
			throw new OAuth2AuthenticationException(new OAuth2Error("invalid_request",
					"OAuth 2.0 Parameter: " + OAuth2ParameterNames.REFRESH_TOKEN, ACCESS_TOKEN_REQUEST_ERROR_URI));
		}

		// 处理请求的作用域
		String scope = request.getParameter(OAuth2ParameterNames.SCOPE);
		Set<String> requestedScopes = null;
		if (StringUtils.hasText(scope)) {
			requestedScopes = new HashSet<>(Arrays.asList(scope.split(" ")));
		}

		Map<String, Object> additionalParameters = new HashMap<>();
		return new OAuth2RefreshTokenAuthenticationToken(refreshToken, null, requestedScopes, additionalParameters);
	}

	private Authentication createClientCredentialsAuthentication(HttpServletRequest request) {
		// 处理请求的作用域
		String scope = request.getParameter(OAuth2ParameterNames.SCOPE);
		Set<String> requestedScopes = null;
		if (StringUtils.hasText(scope)) {
			requestedScopes = new HashSet<>(Arrays.asList(scope.split(" ")));
		}

		Map<String, Object> additionalParameters = new HashMap<>();
		return new OAuth2ClientCredentialsAuthenticationToken(null, requestedScopes, additionalParameters);
	}
}